-- Game
-- 游戏循环主类
Game = class()

-- 初始载入
function Game:init()
    -- 初始化控制器
    if TCMode == ReleaseMode then
        control = Controllor(120,125,220,15)
    else
        control = Controllor(120,145,220,15)
    end
    timer = Timer(20)
    guiRenderer = GuiRenderer()     -- 初始化Gui渲染器
    selector = Selector()       -- 初始化方块栏
    healthBar = Health()    -- 血条
    -- oxygenBar = Oxygen()
    pauseMenu = PauseMenu()     -- 暂停菜单
    frustum = Frustum()     -- 视锥
    soundEngine = SoundEngine() 
    worldSaver = WorldSaver()   -- 世界保存器
    chunkUpdated = 0    -- 更新的区块计数
    lastChunkUpdated = 0    -- 上次更新区块计数
    lastSecondChunkUpdated = 0      -- 上一秒区块更新计数
    self.lastTime = 0
    self.breaking = false   -- 正在破坏方块
    self.prTime = 0     -- 破坏方块之前的延迟
    self.created = false    -- 世界已创建？
    self.touchid = 0
    self.autoRotateVelocity = vec2(0, 0)    -- 自动旋转向量
    self.prevTouchPos = vec2(0, 0)      -- 上一次点击坐标
    self.firstBreaking = false
    self.threadLoop = 1     -- 线程相关
    --[[
    parameter.action("readGame",function() loadScreen = LoadingScreen(true) end)
    parameter.action("saveGame",function() self:saveGame() end)
      ]]
end

-- 创建新地图
function Game:createNewGame()
    local worldInfo = startMenu.mapSizeMenu:getWorldInfo()
    world = World(worldInfo)
    worldRenderer = WorldRenderer()
    world:createNewWorld()
    self:finishCreating()
end

-- 读取地图
function Game:readGame()
    world = worldSaver:read()
    worldRenderer = WorldRenderer()
    world.chunkManager:createChunks()
    self:finishCreating()
end

-- 保存地图
function Game:saveGame()
    worldSaver:save(world)
end

-- 完成创建
function Game:finishCreating()
    pe = ParticleEngine()   -- 粒子引擎
    player = Player()   -- 玩家
    if GameSetting.FancyGraphic then
        clouds = Clouds(128,128,20)
        sky = Sky(100000)
    end
    -- 渲染区块重建线程
    self.runChunkRebuildLoopThread = Thread.create(self.chunkRebuildLoop,self.threadErrorHander)
    -- 区块更新线程
    self.runChunkUpdateLoopThread = Thread.create(self.chunkUpdateLoop,self.threadErrorHander)
    parameter.boolean("showDebug",GameSetting.ShowDebug,function(v) GameSetting.ShowDebug = v end)
    parameter.action("respawn",function() player:resetPos() end)
    parameter.watch("debugInfo.fps")
    parameter.watch("player.destoryingTime")
    parameter.watch("chunkUpdated")
    parameter.boolean("PlaySound",GameSetting.PlaySound,function(v) GameSetting.PlaySound = v end)
    parameter.boolean("enableParticle",GameSetting.EnableParticle,function(v) GameSetting.EnableParticle = v end)
    parameter.boolean("showBob",GameSetting.ShowBob,function(v) GameSetting.ShowBob = v end)
    parameter.action("saveGame",function() WorldSaver(world):save() end)
    for i = 1,10 do
        world.entityList:add(Human(world))  -- 刷Human
    end
    self.created = true
    modManager:setup()  -- 调用Mod的setup函数
end

-- 游戏主循环(虽然叫draw了但是游戏循环来的)
function Game:draw()
    if not self.created then return end
    -- 线程调用处理(可能有bug)
    local loopTimer = os.clock()
    if self.threadLoop % 2 == 0 or GameState == Loading then
        self:runChunkRebuildLoopThread()
        if os.clock() - loopTimer < 0.01 then
            self:runChunkUpdateLoopThread()
        elseif os.clock() - loopTimer > 0.015 then
            print("Chunk Rebuild Time Out!",os.clock() - loopTimer)
        end
    else
        self:runChunkUpdateLoopThread()
        if os.clock() - loopTimer < 0.01 then
            self:runChunkRebuildLoopThread()
        elseif os.clock() - loopTimer > 0.015 then
            print("Chunk Update Time Out!",os.clock() - loopTimer)
        end
    end
    self.threadLoop = self.threadLoop + 1
    -- 计算器更新
    timer:advanceTime()
    if GameState == Gaming then
        for i = 1,timer.ticks do
            -- 更新游戏
            self:tick()
        end
        -- 渲染
        self:render(timer.a)
    elseif GameState == Pause then
        -- 渲染
        self:render(timer.a)
    end
    if ElapsedTime - self.lastTime > 1 then
        tc,fpsCount = 0,0
        lastSecondChunkUpdated = 0
        lastSecondChunkUpdated = lastSecondChunkUpdated + chunkUpdated - lastChunkUpdated
        lastChunkUpdated = chunkUpdated
        self.lastTime = ElapsedTime
    end
end

-- 触摸处理函数
function Game:touched(touch)
    if guiRenderer:touched(touch) then return end
    if touch.state == BEGAN then
        if self.touchid == 0 then
            self.prevTouchPos = vec2(touch.x, touch.y)
            self.touchid = touch.id
        end
    elseif touch.state == MOVING then
        --[[local len = vec2(touch.deltaX,touch.deltaY):len()
        if len > 0.1 then
            player:turn(-touch.deltaY * 0.3,touch.deltaX * 0.3)
        end]]
        if self.touchid == touch.id then
            local delta = (vec2(touch.x, touch.y) - self.prevTouchPos) / 2
            player:turn(-delta.y,delta.x)
            self.autoRotateVelocity = vec2(touch.x, touch.y) - self.prevTouchPos
            self.prevTouchPos = vec2(touch.x, touch.y)
        end
    else
        self.touchid = 0
    end
    
    local selb = player.selectBlock
    if touch.state == BEGAN and selb then
        self.breaking = true
        self.firstBreaking = true
        self.prTime = ElapsedTime
    elseif touch.state == ENDED and selb then
        self.breaking = false
        self.firstBreaking = false
        player.destoryingTime = 0
    end
    if selb and touch.state == BEGAN and touch.tapCount == 2 then
        -- 方块栏是否有方块
        if selector:hasSelectTile() then
            local xa,ya,za,f = selb.x,selb.y,selb.z,selb.f
            -- 找出哪个面
            if f == Front then
                za = za + 1
                elseif f == Back then
                za = za - 1
                elseif f == Top then
                ya = ya + 1
                elseif f == Bottom then
                ya = ya - 1
                elseif f == Left then
                xa = xa - 1
                else
                xa = xa + 1
            end
            -- 判断选中的位置里没有实体(玩家)
            if world:isFree(AABB(xa,ya,za,xa + 1,ya + 1,za + 1)) then
                -- 放置方块
                world:setBlock(xa,ya,za,selector:getSelectTileId())
                -- 从方块选择条里移出一个方块
                selector:removeOneSelectTile()
            end
        end
    end
    -- 调用Mod的触摸函数
    modManager:touched(touch)
end

-- 游戏渲染函数
function Game:render(a)
    -- 画面旋转处理
    if self.autoRotateVelocity:len() > 0.001 and self.touchid == 0 then
        self.autoRotateVelocity = self.autoRotateVelocity * AutoRotateDelay
        local delta = self.autoRotateVelocity / 2
        player:turn(-delta.y,delta.x)
    end
    
    if self.touchid == 0 then 
        self.breaking = false
        self.firstBreaking = false
        player.destoryingTime = 0
    end
    -- 更新拾取方块
    player:updateSelect(a,(self.prevTouchPos - vec2(WIDTH / 2,HEIGHT / 2)) / 100)

    background(127.5, 204, 255, 255)
    
    -- 设置摄像机位置
    if GameSetting.ShowBob and player:shouldShowBob() then
        Camera.shakeCamera(a)
    end
    Camera.setCameraToPlayer(a)
    -- 计算视锥
    frustum:calculate()
    -- 获得摄像机位置
    local eyePos = Camera.getEyePositionVec3(a)
    
    -- 渲染世界区块(不透明层)
    worldRenderer:render(frustum,1,eyePos)
    
    -- 破坏方块
    local sblock = player.selectBlock
    if sblock and self.breaking then
        if self.firstBreaking then
            if ElapsedTime - self.prTime >= 0.5 then
                if player:destoryBlock() then
                    if player.mode == Player.survivalMode then
                        self.prTime = ElapsedTime
                        self.firstBreaking = false
                    else
                        self.prTime = ElapsedTime
                    end
                end
            end
        else
            if ElapsedTime - self.prTime >= 0.15 then
                if player:destoryBlock() then
                    self.prTime = ElapsedTime
                end
            end
        end
        if sblock.id ~= Block.air.id then
            worldRenderer:renderSelect()
        end
    end
    
    if GameSetting.FancyGraphic then
        pe:render(player,a)     -- 粒子系统渲染
        sky:render()    -- 天空球
        clouds:render(eyePos)   -- 云
    end
    -- 渲染实体们
    world.entityList:render(frustum,a)
    -- 渲染区块(透明层)
    worldRenderer:render(frustum,2,eyePos)
    -- 渲染玩家
    player:render(a)
    
    -- 渲染Gui
    guiRenderer:render()
end

-- 游戏更新
function Game:tick()
    -- world:updateChunk()
    -- self:runChunkUpdateLoopThread()
    player:tick()
    world:tick()
    pe:tick()
    world.entityList:tick()
    world.entityBlockList:tick()
    modManager:tick()
    if GameSetting.PlaySound then
        soundEngine:tick()
    end
end

-- 渲染区块重建线程
function Game:chunkUpdateLoop()
    while true do
        world:updateChunk()
        Thread.wait()
    end
end

-- 区块更新线程
function Game:chunkRebuildLoop()
    while true do
        if GameState == Gaming or GameState == Loading then
            worldRenderer:rebuild()
        end
        Thread.wait()
    end
end

-- 线程错误处理函数
function Game.threadErrorHander(thread,err)
    error("[" .. tostring(thread) .. "] Error:" .. err)
end
